package com.gobang.service.impl;

import com.gobang.common.exception.MatchException;
import com.gobang.common.util.MatchUtils;
import com.gobang.domain.entity.MatchQueueEntry;
import com.gobang.service.QueueManager;
import com.gobang.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 基于Redis的队列管理器实现
 * 使用Redis Sorted Set管理匹配队列，按积分自动排序
 *
 * @author gobang-team
 * @since 1.0.0
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class RedisQueueManager implements QueueManager {

    private final RedisTemplate<String, Object> redisTemplate;
    private final UserService userService;

    // Redis键名
    private static final String MATCH_QUEUE_KEY = "match:queue";
    private static final String USER_QUEUE_INFO_KEY = "match:user:";
    
    // 时间格式化器
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public int addToQueue(Long userId, Integer currentScore) {
        try {
            log.info("用户{}加入匹配队列，积分：{}", userId, currentScore);

            // 检查用户是否已在队列中
            if (isInQueue(userId)) {
                log.warn("用户{}已在匹配队列中", userId);
                throw MatchException.alreadyInQueue();
            }

            // 获取用户信息
            var user = userService.getUserById(userId);
            if (user == null) {
                log.error("用户{}不存在", userId);
                throw MatchException.userStatusError();
            }

            // 将用户加入Redis Sorted Set（以积分作为score）
            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            Boolean added = zSetOps.add(MATCH_QUEUE_KEY, userId.toString(), currentScore);

            if (Boolean.TRUE.equals(added)) {
                // 保存用户队列详细信息
                saveUserQueueInfo(userId, user.getUsername(), currentScore);

                // 获取队列位置
                int position = getQueuePosition(userId);
                log.info("用户{}成功加入匹配队列，位置：{}", userId, position);
                return position;
            } else {
                log.error("用户{}加入匹配队列失败", userId);
                throw MatchException.joinQueueFailed();
            }

        } catch (MatchException e) {
            log.error("用户{}加入匹配队列异常", userId, e);
            throw MatchException.joinQueueFailed();
        } catch (Exception e) {
            log.error("用户{}加入匹配队列异常", userId, e);
            throw e;
        }
    }

    @Override
    public boolean removeFromQueue(Long userId) {
        try {
            log.info("用户{}退出匹配队列", userId);

            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            Long removed = zSetOps.remove(MATCH_QUEUE_KEY, userId.toString());

            if (removed != null && removed > 0) {
                // 删除用户队列详细信息
                removeUserQueueInfo(userId);
                log.info("用户{}成功退出匹配队列", userId);
                return true;
            } else {
                log.warn("用户{}不在匹配队列中", userId);
                return false;
            }

        } catch (Exception e) {
            log.error("用户{}退出匹配队列异常", userId, e);
            return false;
        }
    }

    @Override
    public boolean isInQueue(Long userId) {
        try {
            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            Double score = zSetOps.score(MATCH_QUEUE_KEY, userId.toString());
            return score != null;
        } catch (Exception e) {
            log.error("检查用户{}队列状态异常", userId, e);
            return false;
        }
    }

    @Override
    public int getQueuePosition(Long userId) {
        try {
            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            Long rank = zSetOps.rank(MATCH_QUEUE_KEY, userId.toString());
            return rank != null ? rank.intValue() + 1 : -1; // rank从0开始，位置从1开始
        } catch (Exception e) {
            log.error("获取用户{}队列位置异常", userId, e);
            return -1;
        }
    }

    @Override
    public int getQueueSize() {
        try {
            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            Long size = zSetOps.size(MATCH_QUEUE_KEY);
            return size != null ? size.intValue() : 0;
        } catch (Exception e) {
            log.error("获取队列大小异常", e);
            return 0;
        }
    }

    @Override
    public List<MatchQueueEntry> getWaitingUsers() {
        return getWaitingUsers(-1); // -1表示获取所有用户
    }

    @Override
    public List<MatchQueueEntry> getWaitingUsers(int limit) {
        try {
            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            
            // 获取队列中的用户（按积分排序）
            Set<ZSetOperations.TypedTuple<Object>> userTuples;
            if (limit > 0) {
                userTuples = zSetOps.rangeWithScores(MATCH_QUEUE_KEY, 0, limit - 1);
            } else {
                // 获取所有用户
                userTuples = zSetOps.rangeWithScores(MATCH_QUEUE_KEY, 0, -1);
            }

            // 返回 waitingUsers
            List<MatchQueueEntry> waitingUsers = new ArrayList<>();
            if (userTuples != null) {
                for (ZSetOperations.TypedTuple<Object> tuple : userTuples) {
                    String userIdStr = (String) tuple.getValue();
                    Double score = tuple.getScore();
                    
                    if (userIdStr != null && score != null) {
                        Long userId = Long.parseLong(userIdStr);
                        MatchQueueEntry entry = buildQueueEntry(userId, score.intValue());
                        if (entry != null) {
                            waitingUsers.add(entry);
                        }
                    }
                }
            }

            log.debug("获取等待队列用户，总数：{}，返回：{}", getQueueSize(), waitingUsers.size());
            return waitingUsers;

        } catch (Exception e) {
            log.error("获取等待用户列表异常", e);
            return new ArrayList<>();
        }
    }

    @Override
    public int removeUsersFromQueue(List<Long> userIds) {
        if (userIds == null || userIds.isEmpty()) {
            return 0;
        }

        int removedCount = 0;
        for (Long userId : userIds) {
            if (removeFromQueue(userId)) {
                removedCount++;
            }
        }

        log.info("批量移除用户，请求数：{}，成功数：{}", userIds.size(), removedCount);
        return removedCount;
    }

    @Override
    public int cleanupTimeoutUsers(long timeoutSeconds) {
        try {
            // 获取所有等待人员
            List<MatchQueueEntry> allUsers = getWaitingUsers();
            // 所有超时用户
            List<Long> timeoutUserIds = new ArrayList<>();

            for (MatchQueueEntry user : allUsers) {
                // 计算每个用户的等待时间
                long waitTime = user.calculateWaitTime();
                if (waitTime > timeoutSeconds) {
                    timeoutUserIds.add(user.getUserId());
                }
            }

            // 清除超时用户
            int cleanupCount = removeUsersFromQueue(timeoutUserIds);
            log.info("清理超时用户，超时阈值：{}秒，清理数量：{}", timeoutSeconds, cleanupCount);
            return cleanupCount;

        } catch (Exception e) {
            log.error("清理超时用户异常", e);
            return 0;
        }
    }

    @Override
    public QueueStatistics getQueueStatistics() {
        try {
            List<MatchQueueEntry> allUsers = getWaitingUsers();
            
            if (allUsers.isEmpty()) {
                return new QueueStatistics(0, 0, 0, 0, 0.0);
            }

            int totalUsers = allUsers.size();
            long totalWaitTime = allUsers.stream()
                    .mapToLong(MatchQueueEntry::calculateWaitTime)
                    .sum();
            long avgWaitTime = totalWaitTime / totalUsers;

            int minScore = allUsers.stream()
                    .mapToInt(MatchQueueEntry::getCurrentScore)
                    .min().orElse(0);
            int maxScore = allUsers.stream()
                    .mapToInt(MatchQueueEntry::getCurrentScore)
                    .max().orElse(0);
            double avgScore = allUsers.stream()
                    .mapToInt(MatchQueueEntry::getCurrentScore)
                    .average().orElse(0.0);

            QueueStatistics stats = new QueueStatistics(totalUsers, avgWaitTime, minScore, maxScore, avgScore);
            log.debug("队列统计：{}", stats);
            return stats;

        } catch (Exception e) {
            log.error("获取队列统计信息异常", e);
            return new QueueStatistics(0, 0, 0, 0, 0.0);
        }
    }

    @Override
    public MatchQueueEntry getUserQueueEntry(Long userId) {
        if (!isInQueue(userId)) {
            return null;
        }

        try {
            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            Double score = zSetOps.score(MATCH_QUEUE_KEY, userId.toString());
            
            if (score != null) {
                return buildQueueEntry(userId, score.intValue());
            }
        } catch (Exception e) {
            log.error("获取用户{}队列条目异常", userId, e);
        }

        return null;
    }

    @Override
    public boolean updateUserInQueue(Long userId, Integer currentScore) {
        if (!isInQueue(userId)) {
            return false;
        }

        try {
            // 更新Redis中的积分
            ZSetOperations<String, Object> zSetOps = redisTemplate.opsForZSet();
            Boolean updated = zSetOps.add(MATCH_QUEUE_KEY, userId.toString(), currentScore);

            if (Boolean.TRUE.equals(updated)) {
                // 更新用户队列详细信息
                var user = userService.getUserById(userId);
                if (user != null) {
                    saveUserQueueInfo(userId, user.getUsername(), currentScore);
                    log.info("更新用户{}队列信息，新积分：{}", userId, currentScore);
                    return true;
                }
            }
        } catch (Exception e) {
            log.error("更新用户{}队列信息异常", userId, e);
        }

        return false;
    }

    /**
     * 保存用户队列详细信息到Redis
     */
    private void saveUserQueueInfo(Long userId, String username, Integer currentScore) {
        try {
            String key = USER_QUEUE_INFO_KEY + userId;
            String joinTime = LocalDateTime.now().format(TIME_FORMATTER);
            
            redisTemplate.opsForHash().putAll(key, java.util.Map.of(
                    "userId", userId.toString(),
                    "username", username,
                    "currentScore", currentScore.toString(),
                    "joinTime", joinTime
            ));

            // 设置过期时间（30分钟）
            redisTemplate.expire(key, java.time.Duration.ofMinutes(30));
        } catch (Exception e) {
            log.error("保存用户{}队列信息异常", userId, e);
        }
    }

    /**
     * 删除用户队列详细信息
     */
    private void removeUserQueueInfo(Long userId) {
        try {
            String key = USER_QUEUE_INFO_KEY + userId;
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("删除用户{}队列信息异常", userId, e);
        }
    }

    /**
     * 构建队列条目对象
     */
    private MatchQueueEntry buildQueueEntry(Long userId, Integer currentScore) {
        try {
            String key = USER_QUEUE_INFO_KEY + userId;
            Object usernameObj = redisTemplate.opsForHash().get(key, "username");
            Object joinTimeObj = redisTemplate.opsForHash().get(key, "joinTime");

            String username = usernameObj != null ? usernameObj.toString() : "Unknown";
            LocalDateTime joinTime;
            
            if (joinTimeObj != null) {
                joinTime = LocalDateTime.parse(joinTimeObj.toString(), TIME_FORMATTER);
            } else {
                joinTime = LocalDateTime.now(); // 默认为当前时间
            }

            return MatchQueueEntry.builder()
                    .userId(userId)
                    .username(username)
                    .currentScore(currentScore)
                    .joinTime(joinTime)
                    .build();

        } catch (Exception e) {
            log.error("构建用户{}队列条目异常", userId, e);
            return null;
        }
    }
} 